﻿
using Newtonsoft.Json;
using System;
using System.Diagnostics;
using System.Reflection;

namespace CSConfig
{
    public static class Parser
    {
#if true
        public static void UpdateConfigBaseProperties(IItem cfgItem)
        {
            var t = cfgItem.GetType();
            var gt = t.GetGenericTypeDefinition();
            var ps = t.GetProperties();
            foreach (var p in ps)
            {
                if (p.Name == "Value")
                    continue;
                var v=p.GetValue(cfgItem);
                p.SetValue(cfgItem, v);
            }

            var cfgValueObj = valuePropertyInfo.GetValue(cfgItem);
            valuePropertyInfo.SetValue(guiItem, cfgValueObj);
        }
        public static void UpdateChoice(Choice guiChoiceItem, Choice cfgChoiceItem)
        {
            /*
            var t = guiChoiceItem.GetType();
            for (var i = 0; i < cfgChoiceItem.Items.Count; i++)
            {
                var cfg_sub_Item = cfgChoiceItem.Items[i];
                var sub_item_t = cfg_sub_Item.GetType();
                var sub_item_gt = sub_item_t.GetGenericTypeDefinition();
                if (sub_item_gt == typeof(Config<>))
                {
                    UpdateConfig(guiChoiceItem.Items.Count < (i + 1) ? null : guiChoiceItem.Items[i], cfg_sub_Item);
                }
                else if (sub_item_t == typeof(ListItem))
                {
                    var sub_sub_item = (ListItem)cfg_sub_Item;
                    UpdateListItem(guiChoiceItem.Items.Count < (i + 1) ? null : guiChoiceItem.Items[i], sub_sub_item);
                }
                else if (sub_item_gt == typeof(Choice))
                {
                    var sub_sub_cfg_item= (Choice)cfg_sub_Item;
                    UpdateChoice(guiChoiceItem.Items.Count < (i + 1) ? null : (Choice)guiChoiceItem.Items[i], sub_sub_cfg_item);
                }
            }
            */
            //UpdateValue(guiChoiceItem.SelectedItem, cfgChoiceItem.SelectedItem);    
        }
        public static void UpdateListItem(IItem guiItem, IItem cfgItem)
        {
            var t = guiItem.GetType();
            var guiListItem = (ListItem)guiItem;
            var cfgListItem = (ListItem)cfgItem;
            for(var i=0;i<cfgListItem.Items.Count;i++)
            {
                
                var cfg_sub_Item=cfgListItem.Items[i];
                //UpdateValue(guiListItem.Items.Count < (i + 1) ? null : guiListItem.Items[i], cfg_sub_Item);
                var sub_item_t = cfg_sub_Item.GetType();
                var sub_item_gt = sub_item_t.GetGenericTypeDefinition();
                /*if (sub_item_gt == typeof(Config<>))
                {
                    UpdateConfig(guiListItem.Items.Count < (i + 1) ? null : guiListItem.Items[i], cfg_sub_Item);
                }
                else if (sub_item_t == typeof(ListItem))
                {
                    var sub_sub_item=(ListItem)cfg_sub_Item;
                    UpdateListItem(guiListItem.Items.Count<(i+1) ? null : guiListItem.Items[i], sub_sub_item);
                }
                else if(sub_item_gt == typeof(Choice))
                {
                    var sub_sub_choice_item = (Choice)cfg_sub_Item;
                    UpdateChoice(guiListItem.Items.Count < (i + 1) ? null :(Choice) guiListItem.Items[i], sub_sub_choice_item);
                }*/
            }
        }
        public static void UpdateIItemBaseProperties(IItem cfgItem)
        {
            var t= cfgItem.GetType();
            var gt=t.GetGenericTypeDefinition();
            if (gt == typeof(Config<>))
            {
                UpdateConfigBaseProperties(guiItem,cfgItem);
            }
            else if (gt == typeof(ListItem))
            {
                var cfgitem_type = cfgItem.GetType();
                var cfgitem_new_instance = (ListItem)UserScriptDescriptionAssembly.CreateInstance(cfgitem_type.FullName);
                UpdateListItem(cfgitem_new_instance, cfgItem);
                guiItem=cfgitem_new_instance;
            }
            else if (gt == typeof(Choice))
            {
                UpdateChoice((Choice)guiItem, (Choice)cfgItem);
            }
            else
            {
                throw new NotSupportedException();
            }
        }
#endif
        public static void ConfigParsing(Assembly CsConfigAssembly, IMenu descriptionMenu,IMenu userConfigMenu, Assembly UserScriptDescriptionAssembly)
        {
            var properties = descriptionMenu.GetType().GetProperties();
            foreach (var property in properties)
            {
                var itemAttr = property.GetCustomAttribute<ItemConfigAttribute>();
                if (itemAttr != null)
                {
                    object oguiv = property.GetValue(descriptionMenu);
                    object ocfgv = null;
                    if (userConfigMenu != null)
                    {
                        ocfgv = property.GetValue(userConfigMenu);
                    }
                    if (property.PropertyType.IsGenericType)
                    {
                        var configType=property.PropertyType.GetGenericTypeDefinition();
                        if (ocfgv != null)
                        {
                            property.SetValue(descriptionMenu, ocfgv);
                            UpdateItemProperty("Help", (IItem)oguiv, (IItem)ocfgv);
                        }
                        descriptionMenu.ItemValueChanged((IItem)oguiv);
                    }
                    else if (property.PropertyType == typeof(ListItem))
                    {
                        var guiv = (ListItem)oguiv;
                        var cfgv = (ListItem)(ocfgv);
                        property.SetValue(guiv, cfgv);
                        //UpdateItemProperty("Help", (IItem)oguiv, (IItem)ocfgv);
                        descriptionMenu.ItemValueChanged(guiv);
                    }
                    else if(property.PropertyType == typeof(Choice))
                    {
                        Choice guiChoice = (Choice)oguiv;
                        Choice cfgChoice = (Choice)(ocfgv);
                        if (cfgChoice != null)
                        {
                            guiChoice.SelectedItem = cfgChoice.SelectedItem;
                        }
                        if (guiChoice.SelectedItem is IMenu menu)
                        {
                            ConfigParsing(CsConfigAssembly, menu, cfgChoice == null ? null : (IMenu)cfgChoice.SelectedItem, UserScriptDescriptionAssembly);
                        }
                        descriptionMenu.ItemValueChanged(guiChoice.SelectedItem);
                    }
                    else if (property.PropertyType.GetInterfaces().Contains(typeof(IMenu)))
                    {
                        var guiv = (IMenu)oguiv;
                        var cfgv = (IMenu)(ocfgv);
                        if(guiv==null)
                        {
                            guiv = (IMenu)UserScriptDescriptionAssembly.CreateInstance(property.PropertyType.FullName);
                            property.SetValue(descriptionMenu, guiv);
                        }
                        //Debug.WriteLine($"Menu:{subMenu.Name}");
                        ConfigParsing(CsConfigAssembly, guiv, cfgv, UserScriptDescriptionAssembly);
                    }
                }
            }
        }
        public static (IMenu, IMenu) Parse(Assembly UserScriptDescriptionAssembly, string UserConfigFilePath)
        {
            var CsConfigAssembly=Assembly.GetExecutingAssembly();
            var allTypes = UserScriptDescriptionAssembly.GetTypes();
            IMenu mainMenu = null;
            Type mainMenuType = null;
            IMenu userConfig = null;

            foreach (var type in allTypes)
            {
                var mainMenuAttr = type.GetCustomAttribute<MainMenuAttribute>();
                if (mainMenuAttr != null)
                {
                    mainMenuType = type;
                    try
                    {
                        var userConfigText = File.ReadAllText(UserConfigFilePath);
                        var settings = new JsonSerializerSettings
                        {
                            TypeNameHandling = TypeNameHandling.Objects, // 必须与序列化时一致  
                            ObjectCreationHandling= ObjectCreationHandling.Replace,

                        };
                        userConfig = (IMenu)JsonConvert.DeserializeObject(userConfigText, type, settings);
                    }
                    catch (Exception)
                    {
                        Console.WriteLine("cs config type != user config type!");
                    }

                    var mainMenuProperties = type.GetProperties();
                    foreach (var p in mainMenuProperties)
                    {
                        if ( p.PropertyType == type )
                        {
                            mainMenu = (IMenu)(p.GetValue(null));
                            if (mainMenu == null)
                            {
                                Console.WriteLine("please define static menu object!!");
                                return (null, userConfig);
                            }
                            if(userConfig!=null)
                            {
                                ConfigParsing(CsConfigAssembly, mainMenu, userConfig, UserScriptDescriptionAssembly);
                            }
                            
                            return (mainMenu, userConfig);
                        }
                    }
                    break;
                }
            }

            return (null, null);
        }
    
        public static void RecursiveMenu(IMenu descriptionMenu,Action<IMenu, IItem> action)
        {
            var properties = descriptionMenu.GetType().GetProperties();
            foreach (var property in properties)
            {
                var itemAttr = property.GetCustomAttribute<ItemConfigAttribute>();
                if (itemAttr != null)
                {
                    var propertyValueOfMenu = (IItem)property.GetValue(descriptionMenu);

                    if (property.PropertyType.IsGenericType)
                    {
                        var configType = property.PropertyType.GetGenericTypeDefinition();
                        if (configType == typeof(Config<>))
                        {
                            action(descriptionMenu, propertyValueOfMenu);
                        }
                    }
                    else if (property.PropertyType == typeof(ListItem))
                    {
                        if (propertyValueOfMenu is IMenu subMenu)
                        {
                            RecursiveMenu(subMenu, action);
                        }
                        else
                        {
                            action(descriptionMenu, propertyValueOfMenu);
                        }

                    }
                    else if (property.PropertyType.GetInterfaces().Contains(typeof(IMenu)))
                    {
                        IMenu subMenu = (IMenu)propertyValueOfMenu;
                        action(descriptionMenu, subMenu);
                        Debug.WriteLine($"Menu:{subMenu.DisplayName}");
                        RecursiveMenu(subMenu, action);
                    }
                }
            }
        }
#if false
        public static void UpdateMenuValue(IMenu descriptionMenu, IMenu userCfgMainMenu)
        {
            var properties = descriptionMenu.GetType().GetProperties();
            foreach (var property in properties)
            {
                var itemAttr = property.GetCustomAttribute<ItemConfigAttribute>();
                if (itemAttr != null)
                {
                    if (property.PropertyType == typeof(Config))
                    {
                        Config cfgv = (Config)property.GetValue(userCfgMainMenu);
                        Config guiv = (Config)property.GetValue(descriptionMenu);
                        if (guiv.ConfigType == ConfigType.Tristate)
                        {
                            guiv.Value = (Tristate)(Convert.ToInt32(cfgv.Value));
                        }
                        else
                        {
                            guiv.Value = cfgv.Value;
                        }
                        descriptionMenu.ItemValueChanged(guiv);
                    }
                    else if (property.PropertyType == typeof(Choice))
                    {
                        Choice cfgv = (Choice)property.GetValue(userCfgMainMenu);
                        Choice guiv = (Choice)property.GetValue(descriptionMenu);
                        guiv.SelectedConfig = cfgv.SelectedConfig;
                        descriptionMenu.ItemValueChanged(guiv);
                    }
                    else if (property.PropertyType.GetInterfaces().Contains(typeof(IMenu)))
                    {
                        IMenu cfgv = (IMenu) property.GetValue(userCfgMainMenu);
                        IMenu guiv = (IMenu)property.GetValue(descriptionMenu);
                        UpdateMenuValue(guiv, cfgv);
                    }
                }
            }
        }
#endif
    }
}
